﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Sockets;
using System.Security.Cryptography;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using XL_TCPFins.Protocols;

namespace XL_TCPFins
{
    public class TCPFins : IDevices
    {
        private ICommunications _comm { get; }
        private List<byte> _handshake = new List<byte>();
        private byte _suffixIP;
        public TCPFins(ICommunications comm, byte suffixIP)
        {
            _comm = comm;
            _handshake.Add(0x46);
            _handshake.Add(0x49);
            _handshake.Add(0x4e);
            _handshake.Add(0x53);
            _handshake.Add(0x00);
            _handshake.Add(0x00);
            _handshake.Add(0x00);
            _handshake.Add(0x0c);
            _handshake.Add(0x00);
            _handshake.Add(0x00);
            _handshake.Add(0x00);
            _handshake.Add(0x00);
            _handshake.Add(0x00);
            _handshake.Add(0x00);
            _handshake.Add(0x00);
            _handshake.Add(0x00);
            _handshake.Add(0x00);
            _handshake.Add(0x00);
            _handshake.Add(0x00);
            _handshake.Add(suffixIP);
            _suffixIP = suffixIP;
        }
        public Task Read()
        {
            throw new NotImplementedException("未实现该方法");
        }

        public byte[] Read(int length)
        {
            throw new NotImplementedException("未实现该方法");
        }

        public TClass? ReadClass<TClass>(int address, TClass obj = null, int count = 3) where TClass : class
        {
            if (obj == null) { obj = Activator.CreateInstance<TClass>(); }
            int classSize = (int)StructToBytes.GetClassSize(obj);
            if (classSize > 1998)
            {
                throw new Exception("超过Fins最大字节数");
            }
            byte[] bytes = new byte[7];
            bytes[0] = 0x01;
            bytes[1] = 0x82;
            bytes[2] = (byte)(address >> 8);
            bytes[3] = (byte)address;
            bytes[4] = 0;
            bytes[5] = (byte)(classSize >> 8);
            bytes[6] = (byte)classSize;
            bytes = _comm.SendToRead(bytes, count);
            if (bytes == null) { return default; }
            int boolSize = 0;
            StructToBytes.FromBytes(obj, bytes, ref boolSize, 0);
            return obj;
        }

        public TValue ReadSingle<TValue>(int address, int length, int count = 3)
        {
            byte[] list = new byte[7];
            list[0] = 0x01;
            list[1] = 0x82;
            list[2] = (byte)(address >> 8);
            list[3] = (byte)address;
            list[4] = 0x00;
            Type type = typeof(TValue);
            if (type.IsArray)
            {
                throw new Exception("请使用ReadClass<TClass>读取数组数据");
            }
            int count_length = 0;
            switch (type.Name)
            {
                case "Int32":
                case "UInt32":
                case "Single":
                    count_length = 2;
                    break;
                case "Double":
                    count_length = 4;
                    break;
                case "Int16":
                case "UInt16":
                    count_length = 1;
                    break;
                case "Byte":
                case "Int64":
                case "UInt64":
                case "String":
                    count_length = length / 2;
                    break;
                case "Boolean":
                    throw new Exception($"没有实现 {type.Name}");
            }
            list[5] = (byte)(count_length >> 8);
            list[6] = (byte)count_length;
            byte[] bytes = _comm.SendToRead(list, 3);
            if (type.Name == "String")
            {
                byte b = bytes[2];
                for (int i = 3; i < bytes[0]; i = i + 2)
                {
                    byte t = bytes[i - 1];
                    bytes[i - 1] = bytes[i];
                    bytes[i] = t;
                }
                return (TValue)(object)Encoding.ASCII.GetString(bytes.Skip(2).ToArray());
            }
            return (TValue)StructToBytes.GetValue(type, bytes, 0);
        }

        public byte[] Write(byte[] buffer)
        {
            throw new NotImplementedException("未实现该方法");
        }

        public bool WriteClass<TClass>(TClass value, int address, int count = 3) where TClass : class
        {
            if (value == null) { throw new Exception("不要传递空对象"); }
            int classSize = (int)StructToBytes.GetClassSize(value);
            if (classSize > 1998)
            {
                throw new Exception("超过Fins最大字节数");
            }
            byte[] array = new byte[classSize];
            List<byte> list = new List<byte>() { 0x02, 0x82 };
            list.Add((byte)(address >> 8));
            list.Add((byte)address);
            list.Add(0);
            int boolSize = 0;
            StructToBytes.ToBytes(value, array, ref boolSize, 0);
            int length = classSize / 2;
            list.Add((byte)(length >> 8));
            list.Add((byte)length);
            list.AddRange(array);
            var bytes = _comm.SendToRead(list.ToArray(), 3);
            return bytes != null;
        }

        public bool WriteSingle(object value, int address, int offset, int length, int count = 3)
        {
            Type type = value.GetType();
            if (type.IsArray)
            {
                throw new Exception("请使用WriteClass<TClass>方法写入数组数据");
            }
            List<byte> list = new List<byte>() { 0x02, 0x82 };
            list.Add((byte)(address >> 8));
            list.Add((byte)address);
            list.Add((byte)offset);
            byte[] bytes = null;
            switch (type.Name)
            {
                case "Int32":
                case "UInt32":
                case "Single":
                    bytes = new byte[4];
                    break;
                case "Double":
                    bytes = new byte[8];
                    break;
                case "Int16":
                case "UInt16":
                    bytes = new byte[2];
                    break;
                case "Byte":
                case "Int64":
                case "UInt64":
                case "Boolean":
                    throw new Exception($"没有实现 {type.Name}");
                case "String":
                    byte[] code = Encoding.ASCII.GetBytes((string)value);
                    List<byte> list2 = new List<byte>();
                    list2.Add((byte)length);
                    list2.Add((byte)code.Length);
                    list2.AddRange(code);
                    if (list2.Count % 2 != 0)
                    {
                        list2.Add(0);
                    }
                    for (int i = 3; i < list2.Count; i = i + 2)
                    {
                        byte t = list2[i - 1];
                        list2[i - 1] = list2[i];
                        list2[i] = t;
                    }
                    bytes = list2.ToArray();
                    break;
            }
            int length1 = (bytes.Length / 2);
            list.Add((byte)(length1 >> 8));
            list.Add((byte)length1);
            if (type.Name != "String")
            {
                StructToBytes.GetBytes(value, bytes, 0);
            }
            list.AddRange(bytes);
            bytes = _comm.SendToRead(list.ToArray(), 3);
            return bytes != null;
        }

        public bool Open()
        {
            _comm.Open();
            TCP_Fins_Protocol protocol = new TCP_Fins_Protocol();
            _comm.Write(protocol.GetRegistrationMessage(_suffixIP));
            Thread.Sleep(5);
            byte[] bytes = _comm.Read(24);
            int error = bytes.Skip(12).Take(4).ToArray().ByteToInt();
            if (error == 0)
            {
                protocol.SA1 = _suffixIP;
                protocol.DA1 = bytes.Skip(23).ToArray()[0];
                _comm.SetProtocol(protocol);
                return true;
            }
            return false;
        }

        public void Close() => _comm.Close();
    }
}
